package com.gelvt.minissm.spring.beans.factory;

import com.gelvt.minissm.spring.beans.BeansException;
import com.gelvt.minissm.spring.lang.Nullable;

/**
 * Bean工厂根接口
 * @author: Elvin Zeng
 * @date: 17-7-10
 */
public interface BeanFactory {

    /**
     * 当被引用的bean本身是一个FactoryBean时，如果加上这个前缀，则会返回FactoryBean本身。
     * 反之，则会返回FactoryBean创建的bean实例。
     */
    String FACTORY_BEAN_PREFIX = "&";


    /**
     * 根据bean的名称获取bean实例。
     * @param name bean的名称
     * @return bean实例
     * @throws NoSuchBeanDefinitionException 如果找不到指定名称的bean的定义
     * @throws BeansException 如果获取不到指定的bean
     */
    Object getBean(String name) throws BeansException;

    /**
     * 根据bean的名称获取bean的实例，并确保bean是指定的类型的实例。
     * @param name bean的名称
     * @param requiredType 指定bean的类型。可以使bean的实际类型的超类。
     * @return bean实例
     * @throws NoSuchBeanDefinitionException 如果找不到指定名称的bean的定义
     * @throws BeanNotOfRequiredTypeException 如果对应的bean并不属于指定的类型
     * @throws BeansException 如果获取不到指定的bean
     */
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

    /**
     * 获取bean实例。
     * 允许指定构造函数参数或者工厂方法参数。如果在bean的定义中已经存在这些参数，则将覆盖默认参数。
     * @param name bean名称
     * @param args 创建bean的时候的参数名（作用于新创建实例的时候）
     * @return bean实例
     * @throws NoSuchBeanDefinitionException 如果找不到指定名称的bean的定义
     * @throws BeanDefinitionStoreException 如果已经给出了参数，但是受影响的bean并不是prototype类型的bean。
     * @throws BeansException 如果bean无法被创建
     */
    Object getBean(String name, Object... args) throws BeansException;

    /**
     * 获取匹配且唯一匹配给定类型的bean。
     * @param requiredType bean的类型，可以是超类。
     * @return 唯一匹配指定类型的bean
     * @throws NoSuchBeanDefinitionException 如果找不到bean
     * @throws NoUniqueBeanDefinitionException 如果匹配指定类型的bean超过一个
     * @throws BeansException 如果bean不能被创建
     */
    <T> T getBean(Class<T> requiredType) throws BeansException;

    /**
     * 获取匹配给定类型的bean。
     * 允许指定构造函数参数或者工厂方法参数。如果在bean的定义中已经存在这些参数，则将覆盖默认参数。
     * @param requiredType bean的类型，可以是超类。
     * @param args 创建bean的时候的参数名（作用于新创建实例的时候）
     * @return 唯一匹配指定类型的bean
     * @throws NoSuchBeanDefinitionException 如果找不到bean
     * @throws BeanDefinitionStoreException 如果已经给出了参数，但是受影响的bean并不是prototype类型的bean。
     * @throws BeansException 如果bean不能被创建
     */
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    /**
     * 指示是否包含指定名称或者别名的bean定义。
     * 如果当前工厂包含层次关系，当找不到bean时将会去父工厂查找。
     * 这个函数只指示是否包含bean的定义，但是并不确保这个bean一定可以被获取。
     * @param name bean名称
     * @return 给定名称的bean是否存在
     */
    boolean containsBean(String name);

    /**
     * 指示给定名称的bean是否是共享的单实例bean。
     * 当找不到bean时将会去父工厂查找。
     * @param name bean名称
     * @return 指定的bean是否是一个Singleton bean
     * @throws NoSuchBeanDefinitionException 如果指定的bean不存在
     */
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    /**
     * 指示给定名称的bean是否是prototype bean。
     * 当找不到bean时将会去父工厂查找。
     * @param name bean名称
     * @return 当前bean是否是一个prototype类型的bean
     * @throws NoSuchBeanDefinitionException 如果指定的bean不存在
     */
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // TODO 待翻译

    /**
     * Check whether the bean with the given name matches the specified type.
     * More specifically, check whether a {@link #getBean} call for the given name
     * would return an object that is assignable to the specified target type.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @param typeToMatch the type to match against (as a {@code ResolvableType})
     * @return {@code true} if the bean type matches,
     * {@code false} if it doesn't match or cannot be determined yet
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 4.2
     * @see #getBean
     * @see #getType
     */
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * Check whether the bean with the given name matches the specified type.
     * More specifically, check whether a {@link #getBean} call for the given name
     * would return an object that is assignable to the specified target type.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @param typeToMatch the type to match against (as a {@code Class})
     * @return {@code true} if the bean type matches,
     * {@code false} if it doesn't match or cannot be determined yet
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 2.0.1
     * @see #getBean
     * @see #getType
     */
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * Determine the type of the bean with the given name. More specifically,
     * determine the type of object that {@link #getBean} would return for the given name.
     * <p>For a {@link FactoryBean}, return the type of object that the FactoryBean creates,
     * as exposed by {@link FactoryBean#getObjectType()}.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @return the type of the bean, or {@code null} if not determinable
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 1.1.2
     * @see #getBean
     * @see #isTypeMatch
     */
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    /**
     * Return the aliases for the given bean name, if any.
     * All of those aliases point to the same bean when used in a {@link #getBean} call.
     * <p>If the given name is an alias, the corresponding original bean name
     * and other aliases (if any) will be returned, with the original bean name
     * being the first element in the array.
     * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the bean name to check for aliases
     * @return the aliases, or an empty array if none
     * @see #getBean
     */
    String[] getAliases(String name);


}
